package com.oa.tools.utils.oa;

import java.io.IOException;
import java.io.InputStream;

import com.oa.tools.utils.StringUtil;

public abstract class AbstractInputStream extends InputStream
{
	static String tempString = "1"; //0 不加密  //1加密，兼容不加密 //2不加密，兼容加密
  protected static final String attCrypMode = StringUtil.isBlank(tempString) ? "0":tempString;
  protected int cryptionMode;
  protected static final byte[] markBytes = { 45, -89, -1, 61, -57, 5, -15, 
    1, -126, 8, 90, 125, -79, -14, 20, 92, 29, -11, 8, -42, 2, -64, 
    -12, 107, 3, -109, -49, -3, -118, 120, 74, -82 };

  protected byte[] additionalBytes = null;

  private int count = 0;
  protected InputStream originStream;
  private int pos = 0;

  public AbstractInputStream(InputStream originStream) throws IOException
  {
    this(originStream, Integer.parseInt(attCrypMode));
  }

  public AbstractInputStream(InputStream originStream, int cryptionMode) throws IOException
  {
    this.cryptionMode = cryptionMode;
    this.originStream = originStream;
    init();
    if (this.additionalBytes == null)
      this.count = 0;
    else
      this.count = this.additionalBytes.length;
  }

  protected abstract void init()
    throws IOException;

  protected abstract void format(byte[] paramArrayOfByte, int paramInt1, int paramInt2);

  protected abstract int format(int paramInt);

  public int read()
    throws IOException
  {
    if (this.pos == this.count)
      return format(this.originStream.read());
    return format(this.additionalBytes[(this.pos++)]);
  }

  public int available()
    throws IOException
  {
    return this.originStream.available() + this.count - this.pos;
  }

  public void close()
    throws IOException
  {
    if (this.originStream != null)
      this.originStream.close();
  }

  public void mark(int readlimit)
  {
    this.originStream.mark(readlimit);
  }

  public boolean markSupported()
  {
    return this.originStream.markSupported();
  }

  public int read(byte[] b)
    throws IOException
  {
    if (this.pos == this.count) {
      int result = this.originStream.read(b);
      format(b, 0, result);
      return result;
    }
    return read(b, 0, b.length);
  }

  public int read(byte[] b, int off, int len)
    throws IOException
  {
    if (this.pos == this.count) {
      int result = this.originStream.read(b, off, len);
      format(b, off, result);
      return result;
    }
    int len1 = this.count - this.pos;
    int len2 = 0;
    if (len1 >= len)
      len1 = len;
    else {
      len2 = len - len1;
    }
    System.arraycopy(this.additionalBytes, this.pos, b, off, len1);
    this.pos += len1;
    if (len2 > 0) {
      if (this.originStream.available() == 0)
        len2 = 0;
      else
        len2 = this.originStream.read(b, off + len1, len2);
    }
    int result = len1 + len2;
    format(b, off, result);
    return result;
  }

  public void reset()
    throws IOException
  {
    this.originStream.reset();
  }

  public long skip(long n)
    throws IOException
  {
    if (this.pos == this.count)
      return this.originStream.skip(n);
    long n1 = this.count - this.pos;
    long n2 = 0L;
    if (n1 >= n)
      n1 = n;
    else {
      n2 = n - n1;
    }
    this.pos = ((int)(this.pos + n1));
    if (n2 > 0L) {
      n2 = this.originStream.skip(n2);
    }
    return n1 + n2;
  }

  private static AbstractInputStream createInputStream(InputStream originStream, int cryptionMode, String cryptionMethod)
    throws IOException
  {
    AbstractInputStream result = null;
//    if (cryptionMethod.equals("encrypt")) {
//      result = new EncryptionInputStream(originStream, cryptionMode);
//    }
    if (cryptionMethod.equals("decrypt")) {
      result = new DecryptionInputStream(originStream, cryptionMode);
    }
    return result;
  }

  public static AbstractInputStream createDecryptStream(InputStream originStream, int cryptionMode) throws IOException
  {
    return createInputStream(originStream, cryptionMode, "decrypt");
  }

  public static AbstractInputStream createEncryptStream(InputStream originStream, int cryptionMode) throws IOException
  {
    return createInputStream(originStream, cryptionMode, "encrypt");
  }
}